/*
- * io.c: handling I/O, interrupts related VMX entry/exit
+ * intr.c: handling I/O, interrupts related VMX entry/exit
* Copyright (c) 2004, Intel Corporation.
+ * Copyright (c) 2004-2007, XenSource Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
- *
*/
#include <xen/config.h>
#include <xen/errno.h>
#include <xen/trace.h>
#include <xen/event.h>
-
#include <asm/current.h>
#include <asm/cpufeature.h>
#include <asm/processor.h>
#include <public/hvm/ioreq.h>
#include <asm/hvm/trace.h>
+/*
+ * A few notes on virtual NMI and INTR delivery, and interactions with
+ * interruptibility states:
+ *
+ * We can only inject an ExtInt if EFLAGS.IF = 1 and no blocking by
+ * STI nor MOV SS. Otherwise the VM entry fails. The 'virtual interrupt
+ * pending' control causes a VM exit when all these checks succeed. It will
+ * exit immediately after VM entry if the checks succeed at that point.
+ *
+ * We can only inject an NMI if no blocking by MOV SS (also, depending on
+ * implementation, if no blocking by STI). If pin-based 'virtual NMIs'
+ * control is specified then the NMI-blocking interruptibility flag is
+ * also checked. The 'virtual NMI pending' control (available only in
+ * conjunction with 'virtual NMIs') causes a VM exit when all these checks
+ * succeed. It will exit immediately after VM entry if the checks succeed
+ * at that point.
+ *
+ * Because a processor may or may not check blocking-by-STI when injecting
+ * a virtual NMI, it will be necessary to convert that to block-by-MOV-SS
+ * before specifying the 'virtual NMI pending' control. Otherwise we could
+ * enter an infinite loop where we check blocking-by-STI in software and
+ * thus delay delivery of a virtual NMI, but the processor causes immediate
+ * VM exit because it does not check blocking-by-STI.
+ *
+ * Injecting a virtual NMI sets the NMI-blocking interruptibility flag only
+ * if the 'virtual NMIs' control is set. Injecting *any* kind of event clears
+ * the STI- and MOV-SS-blocking interruptibility-state flags.
+ *
+ * If MOV/POP SS is executed while MOV-SS-blocking is in effect, the effect
+ * is cleared. If STI is executed while MOV-SS- or STI-blocking is in effect,
+ * the effect is cleared. (i.e., MOV-SS-blocking 'dominates' STI-blocking).
+ */
-static inline void
-enable_irq_window(struct vcpu *v)
+static void enable_irq_window(struct vcpu *v)
{
u32 *cpu_exec_control = &v->arch.hvm_vcpu.u.vmx.exec_control;
- if (!(*cpu_exec_control & CPU_BASED_VIRTUAL_INTR_PENDING)) {
+ if ( !(*cpu_exec_control & CPU_BASED_VIRTUAL_INTR_PENDING) )
+ {
*cpu_exec_control |= CPU_BASED_VIRTUAL_INTR_PENDING;
__vmwrite(CPU_BASED_VM_EXEC_CONTROL, *cpu_exec_control);
}
}
-static inline void
-disable_irq_window(struct vcpu *v)
-{
- u32 *cpu_exec_control = &v->arch.hvm_vcpu.u.vmx.exec_control;
-
- if ( *cpu_exec_control & CPU_BASED_VIRTUAL_INTR_PENDING ) {
- *cpu_exec_control &= ~CPU_BASED_VIRTUAL_INTR_PENDING;
- __vmwrite(CPU_BASED_VM_EXEC_CONTROL, *cpu_exec_control);
- }
-}
-
-static inline int is_interruptibility_state(void)
-{
- return __vmread(GUEST_INTERRUPTIBILITY_INFO);
-}
-
static void update_tpr_threshold(struct vlapic *vlapic)
{
int max_irr, tpr;
asmlinkage void vmx_intr_assist(void)
{
- int intr_type = 0;
- int intr_vector;
- unsigned long eflags;
+ int has_ext_irq, intr_vector, intr_type = 0;
+ unsigned long eflags, intr_shadow;
struct vcpu *v = current;
unsigned int idtv_info_field;
unsigned long inst_len;
- int has_ext_irq;
pt_update_irq(v);
inst_len = __vmread(VM_EXIT_INSTRUCTION_LEN); /* Safe */
__vmwrite(VM_ENTRY_INSTRUCTION_LEN, inst_len);
- if (unlikely(idtv_info_field & 0x800)) /* valid error code */
+ if ( unlikely(idtv_info_field & 0x800) ) /* valid error code */
__vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE,
__vmread(IDT_VECTORING_ERROR_CODE));
- if (unlikely(has_ext_irq))
+ if ( unlikely(has_ext_irq) )
enable_irq_window(v);
HVM_DBG_LOG(DBG_LEVEL_1, "idtv_info_field=%x", idtv_info_field);
if ( likely(!has_ext_irq) )
return;
- if ( unlikely(is_interruptibility_state()) )
+ intr_shadow = __vmread(GUEST_INTERRUPTIBILITY_INFO);
+ if ( unlikely(intr_shadow & (VMX_INTR_SHADOW_STI|VMX_INTR_SHADOW_MOV_SS)) )
{
- /* pre-cleared for emulated instruction */
enable_irq_window(v);
HVM_DBG_LOG(DBG_LEVEL_1, "interruptibility");
return;
static void inline __update_guest_eip(unsigned long inst_len)
{
- unsigned long current_eip;
+ unsigned long current_eip, intr_shadow;
current_eip = __vmread(GUEST_RIP);
__vmwrite(GUEST_RIP, current_eip + inst_len);
- __vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0);
+
+ intr_shadow = __vmread(GUEST_INTERRUPTIBILITY_INFO);
+ if ( intr_shadow & (VMX_INTR_SHADOW_STI | VMX_INTR_SHADOW_MOV_SS) )
+ {
+ intr_shadow &= ~(VMX_INTR_SHADOW_STI | VMX_INTR_SHADOW_MOV_SS);
+ __vmwrite(GUEST_INTERRUPTIBILITY_INFO, intr_shadow);
+ }
}
static void vmx_do_no_device_fault(void)
case EXIT_REASON_TRIPLE_FAULT:
hvm_triple_fault();
break;
- case EXIT_REASON_PENDING_INTERRUPT:
+ case EXIT_REASON_PENDING_VIRT_INTR:
/* Disable the interrupt window. */
v->arch.hvm_vcpu.u.vmx.exec_control &= ~CPU_BASED_VIRTUAL_INTR_PENDING;
__vmwrite(CPU_BASED_VM_EXEC_CONTROL,
v->arch.hvm_vcpu.u.vmx.exec_control);
break;
+ case EXIT_REASON_PENDING_VIRT_NMI:
+ /* Disable the NMI window. */
+ v->arch.hvm_vcpu.u.vmx.exec_control &= ~CPU_BASED_VIRTUAL_NMI_PENDING;
+ __vmwrite(CPU_BASED_VM_EXEC_CONTROL,
+ v->arch.hvm_vcpu.u.vmx.exec_control);
+ break;
case EXIT_REASON_TASK_SWITCH:
goto exit_and_crash;
case EXIT_REASON_CPUID:
#define CPU_BASED_CR8_LOAD_EXITING 0x00080000
#define CPU_BASED_CR8_STORE_EXITING 0x00100000
#define CPU_BASED_TPR_SHADOW 0x00200000
+#define CPU_BASED_VIRTUAL_NMI_PENDING 0x00400000
#define CPU_BASED_MOV_DR_EXITING 0x00800000
#define CPU_BASED_UNCOND_IO_EXITING 0x01000000
#define CPU_BASED_ACTIVATE_IO_BITMAP 0x02000000
#define PIN_BASED_EXT_INTR_MASK 0x00000001
#define PIN_BASED_NMI_EXITING 0x00000008
+#define PIN_BASED_VIRTUAL_NMIS 0x00000020
extern u32 vmx_pin_based_exec_control;
#define VM_EXIT_IA32E_MODE 0x00000200
(vmx_cpu_based_exec_control & CPU_BASED_ACTIVATE_MSR_BITMAP)
extern char *vmx_msr_bitmap;
-/* VMCS Encordings */
+/* GUEST_INTERRUPTIBILITY_INFO flags. */
+#define VMX_INTR_SHADOW_STI 0x00000001
+#define VMX_INTR_SHADOW_MOV_SS 0x00000002
+#define VMX_INTR_SHADOW_SMI 0x00000004
+#define VMX_INTR_SHADOW_NMI 0x00000008
+
+/* VMCS field encodings. */
enum vmcs_field {
GUEST_ES_SELECTOR = 0x00000800,
GUEST_CS_SELECTOR = 0x00000802,
#define EXIT_REASON_SIPI 4
#define EXIT_REASON_IO_SMI 5
#define EXIT_REASON_OTHER_SMI 6
-#define EXIT_REASON_PENDING_INTERRUPT 7
-
+#define EXIT_REASON_PENDING_VIRT_INTR 7
+#define EXIT_REASON_PENDING_VIRT_NMI 8
#define EXIT_REASON_TASK_SWITCH 9
#define EXIT_REASON_CPUID 10
#define EXIT_REASON_HLT 12
{
unsigned long intr_fields;
- /* Reflect it back into the guest */
+ /*
+ * NB. Callers do not need to worry about clearing STI/MOV-SS blocking:
+ * "If the VM entry is injecting, there is no blocking by STI or by
+ * MOV SS following the VM entry, regardless of the contents of the
+ * interruptibility-state field [in the guest-state area before the
+ * VM entry]", PRM Vol. 3, 22.6.1 (Interruptibility State).
+ */
+
intr_fields = (INTR_INFO_VALID_MASK | type | trap);
if ( error_code != VMX_DELIVER_NO_ERROR_CODE ) {
__vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
static inline void vmx_inject_extint(struct vcpu *v, int trap, int error_code)
{
__vmx_inject_exception(v, trap, INTR_TYPE_EXT_INTR, error_code, 0);
- __vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0);
}
#endif /* __ASM_X86_HVM_VMX_VMX_H__ */